#include "stm32f10x.h"                  // Device header
#include "MPU6050Reg.h"

#define I2C2_SCL GPIO_Pin_10
#define I2C2_SDA GPIO_Pin_11

/*硬件I2C与MPU6050通信*/

#define MPU6050_Address 0xD0

uint8_t MPU6050_ReceiveData[100] = { 0 };
uint8_t MPU6050_WriteData[100] = { 0 };
int16_t MPU6050_Stateinfo[7] = { 0 };           //前3为存储Ax Ay Az 第4位'C 第五位Gx Gy Gz

/// @brief 内含超时退出的等待事件函数
/// @param I2CX 
/// @param Event 
void MPU6050_WaitEvent(I2C_TypeDef *I2CX,uint32_t Event)
{
    uint32_t TimeOut = 0;
    while (I2C_CheckEvent(I2CX, Event) != SUCCESS)
    {
        TimeOut++;
        if (TimeOut == 100000)
        {
            break;
        }
    }
}


//指定地址写入 一个字节
void MPU6050_WriteReg_Single(uint8_t RegAddress, uint8_t Data)
{
    I2C_GenerateSTART(I2C2, ENABLE);                                                        //产生起始信号
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                                  //等待起始信号产生

    I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Transmitter);                  //发送设备地址 写
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);                    //等待设备地址发送成功

    I2C_SendData(I2C2, RegAddress);                                                         //发送寄存器地址
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);                            //等待寄存器地址发送成功 EV8

    I2C_SendData(I2C2, Data);                                                               //发送数据
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);                             //等待数据发送成功 (最后一个数据发送成功才使用TRASMITTED)EV8_2

    I2C_GenerateSTOP(I2C2, ENABLE);                                                         //产生停止信号

}

//指定地址写入 一组数据
void MPU6050_WriteReg_Multiply(uint8_t RegAddress, uint8_t *Data, uint16_t Count)
{
    I2C_GenerateSTART(I2C2, ENABLE);                                                        //产生起始信号
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                                  //等待起始信号产生

    I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Transmitter);                  //发送设备地址 写
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);                    //等待设备地址发送成功

    I2C_SendData(I2C2, RegAddress);                                                         //发送寄存器地址
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);                             //等待寄存器地址发送成功 这里尽量使用TRANSMITTED

    while (Count--)
    {
        I2C_SendData(I2C2, *Data++);                                                        //发送数据
        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);                         //等待数据发送成功 (最后一个数据发送成功才使用TRASMITTED)EV8_2
    }

    I2C_GenerateSTOP(I2C2, ENABLE);                                                         //产生停止信号
}

//指定地址读 一个字节
uint8_t MPU6050_ReadReg_Single(uint8_t RegAddress)
{
    uint8_t Data = 0;
    I2C_GenerateSTART(I2C2, ENABLE);                                                        //产生起始信号
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                                  //等待起始信号产生

    I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Transmitter);                  //发送设备地址 写   
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);                    //等待设备地址发送成功

    I2C_SendData(I2C2, RegAddress);                                                         //发送寄存器地址
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);                             //等待寄存器地址发送成功 这里尽量使用TRANSMITTED

    I2C_GenerateSTART(I2C2, ENABLE);                                                        //产生起始信号
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                                  //等待起始信号产生

    I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Receiver);                     //发送设备地址 读
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);                       //等待设备地址发送成功
    
    //在接收最后一个数据之前，提前设置NACK和STOP
    I2C_AcknowledgeConfig(I2C2, DISABLE);                                                   //关闭应答
    I2C_GenerateSTOP(I2C2, ENABLE);                                                         //产生停止信号

    //等待数据接收成功，数据就在DR寄存器里面了
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);                                //等待数据接收成功
    Data = I2C_ReceiveData(I2C2);                                                           //读取数据

    //最后一个数据接收成功后，关闭应答，我们希望默认是ACK，方便后续改进代码
    I2C_AcknowledgeConfig(I2C2, ENABLE);                                                    //开启应答
    return Data;
}

//指定地址读 多个字节
void MPU6050_ReadReg_Multiply(uint8_t RegAddress, uint8_t Count)
{
    uint8_t i = 0;
    I2C_GenerateSTART(I2C2, ENABLE);                                                        //产生起始信号
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                                  //等待起始信号产生

    I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Transmitter);                  //发送设备地址 写
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);                    //等待设备地址发送成功

    I2C_SendData(I2C2, RegAddress);                                                         //发送寄存器地址
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);                             //等待寄存器地址发送成功 这里尽量使用TRANSMITTED

    I2C_GenerateSTART(I2C2, ENABLE);                                                        //产生起始信号
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);                                  //等待起始信号产生

    I2C_Send7bitAddress(I2C2, MPU6050_Address, I2C_Direction_Receiver);                     //发送设备地址 读
    MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);                       //等待设备地址发送成功

    while (Count)
    {
        if (Count == 1)
        {
            I2C_AcknowledgeConfig(I2C2, DISABLE);                                           //关闭应答
            I2C_GenerateSTOP(I2C2, ENABLE);                                                 //产生停止信号
        }
        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);                            //等待数据接收成功
        MPU6050_ReceiveData[i] = I2C_ReceiveData(I2C2);                                     //读取数据
        Count--;
        i++;
    }
    I2C_AcknowledgeConfig(I2C2, ENABLE);                                                    //开启应答
}

void MPU6050_Init(void)
{   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);    //开启I2C2时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);   //开启GPIOB时钟

    GPIO_InitTypeDef GPIO_InitStructure = 
    {
        .GPIO_Pin = I2C2_SCL | I2C2_SDA,
        .GPIO_Mode = GPIO_Mode_AF_OD,
        .GPIO_Speed = GPIO_Speed_50MHz
    };
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    
    I2C_InitTypeDef I2C_InitStructure = 
    {
        .I2C_ClockSpeed = 100000,                                   //时钟速度100kHz
        .I2C_Mode = I2C_Mode_I2C,                                   //I2C模式
        .I2C_DutyCycle = I2C_DutyCycle_2,                           //占空比2:1 仅在快速模式有效
        .I2C_OwnAddress1 = 0x00,                                    //本机地址
        .I2C_Ack = I2C_Ack_Enable,                                  //Enable应答
        .I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit     //7位地址
    };
    I2C_Init(I2C2, &I2C_InitStructure);

    I2C_Cmd(I2C2, ENABLE);                                          //Enable I2C2


	MPU6050_WriteReg_Single(MPU6050_PWR_MGMT_1, 0x01);                  //配置电源管理1,解除睡眠，选择陀螺仪时钟
	MPU6050_WriteReg_Single(MPU6050_PWR_MGMT_1, 0x00);                  //配置电源管理2,6个轴均不待机
	MPU6050_WriteReg_Single(MPU6050_SMPLRT_DIV, 0x09);                  //9分频
	MPU6050_WriteReg_Single(MPU6050_CONFIG, 0x06);                      //无外部同步，最高低通滤波
	MPU6050_WriteReg_Single(MPU6050_GYRO_CONFIG, 0x18);                 //陀螺仪，不自测，最大量程
	MPU6050_WriteReg_Single(MPU6050_ACCEL_CONFIG, 0x18);                //加速度，不自测，最大量程
}

uint8_t MPU6050_GetID(void)
{
    return MPU6050_ReadReg_Single(MPU6050_WHO_AM_I);
}



//依次返回Ax Ay Az 温度 Gx Gy Gz 数据在MPU6050_Stateinfo里面
//注意 xyz的数据分高低位存储
void MPU6050_GetData(void)
{
    uint8_t i;
    MPU6050_ReadReg_Multiply(MPU6050_ACCEL_XOUT_H, 14);
    for (i = 0; i < 7; i++)
    {
        MPU6050_Stateinfo[i] = (MPU6050_ReceiveData[2 * i] << 8) | MPU6050_ReceiveData[2 * i + 1];  //高低位合成数据
    }
}

